Persuading Functions To Return Multiple Values

by Tom Nally Steelweaver52@aol.com


NL133 Home

::::::::::::::::::::::::::::::::::::::::::::::::::::

Chat Challenge

Eddie Version 3

Stylebits Corner

Progress Bars

Velleman Interface

Sprite Byte

Simulating BMP Buttons

Program Security

LB Functions

::::::::::::::::::::::::::::::::::::::::::::::::::::

Submission Guildlines

Newsletter Help

Index

The Technique: Convert and Concatenate

Typically, Liberty BASIC programmers write user-defined LB functions with the expectation that the function will return a single value. There are occasions, however, when it would be extremely helpful if a function could return two, three or more values, instead of just a single value.

Fortunately, there is a convenient technique that LB programmers have been using lately to persuade functions to return multiple numeric values. Here's how it works.

  1. Though you may want your function to return numeric values, give your function a name designating it as a string function. Prior to that, establish the location from which the the function is called:

    TwoValues$ = MyFunction$(x,y,z)
    

    Then define the function elsewhere in your program:

    Function MyFunction$(a,b,c)
    ...
    end Function
    

    The dollar sign - $ - at the end of the function name tells LB's compiler that this is a string function.

  2. Inside the function, process your return values as you normally would. Example:

    answer1 = ((-1) * b + SQR(b^2 - 4*a*c)) / (2*a)
    answer2 = ((-1) * b - SQR(b^2 - 4*a*c)) / (2*a)
    

  3. Next, convert your two return values into strings using LB's built-in str$() function:

    answer1$ = str$(answer1)
    answer2$ = str$(answer2)
    

  4. Concatenate the string equivalents of your two return values by using string addition. Also, make sure that you put a separator string between these two return values to enable parsing after the function has done it's job. Example:

    MyFunction$ = answer1$ + "  " + answer2$
    

    MyFunction$ is the variable that is returned to that portion of the program which calls the function. Note that two spaces, " ", are used as the separator string. Because these two spaces separate answer1 and answer2, the two answers can be extracted from the return value by parsing. Parsing can be accomplished by use of Liberty BASIC's built-in word$() and val() functions.

  5. Parse the two desired return values by using word$() and val(). (Recall that when the function was first called, the results were stored in the variable TwoValues$.) Example:

    FirstValue  = val(word$(TwoValues$, 1))
    SecondValue = val(word$(TwoValues$, 2))
    

That's it! Mission accomplished.

What to Avoid

The purpose of persuading a function to return two values is to avoid writing -- and calling -- two separate functions which return a single value each, particularly if the two functions would be nearly identical anyway. Though it may not be true in all cases, the expectation is that use of a single function will be faster despite that fact that the answer must be parsed once the function has done its job.

With speed in mind, I am hypothesizing that efficiency can be gained if the function is called -- and the returned value parsed -- by using the following procedure:

TwoValues$ = MyFunction$(x,y,z)
FirstValue  = val(word$(TwoValues$, 1))
SecondValue = val(word$(TwoValues$, 2))

In the example above, the return value is stored in TwoValues$, and then the two desired answers are parsed from the return value. Contrast that procedure with this less-efficient one:

FirstValue  = val(word$(MyFunction$(x,y,z), 1))
SecondValue = val(word$(MyFunction$(x,y,z), 2))

Note that the second procedure is expected to be less efficient because it calls the function MyFunction$() twice! The second procedure defeats the purpose of writing a function which returns two values, because the program ends up making two function calls anyway.

The speed difference may not be noticeable if the function is not math intensive, or if the function is called very few times in the program anyway. However, if the function is long and complicated, and/or if the function is called thousands of times during program operation, the speed might be noticeable. (See the article discussing ScreenX() and ScreenY() for an example of two math-intensive, nearly-identical functions which are called thousands of times during the creation of 3D wire frame models.)

Using the Technique to Obtain the Coordinates of a Projectile in Space

In Liberty BASIC Newsletter 130, I contributed an article called Projectile Motion in 3D Space. This article discussed the mathematics for calculating the x-, y- and z-coordinates of a projectile at any time, t, given the projectile's launch location, initial angle, and initial velocity.

This set of equations can be made much more usable if we employ the Convert and Concatenate technique to write a custom function. In this function, the seven arguments will be as follows:

  1. The x-coordinate of the launch location

  2. The y-coordinate of the launch location

  3. The z-coordinate of the launch location

  4. The swivel angle

  5. The tilt angle

  6. The muzzle velocity

  7. The time at which we desire the coordinates of the fired projectile

This custom function will return the x-, y- and z-coordinates of the projectile's position in space as a single string that can be parsed into three separate values. Thus, even those who are not physics-savvy can write Liberty BASIC programs involving missiles, basketballs and expectorated watermelon seeds!

To obtain the source code for this function, go here.

Other Ways In Which Functions Can Write to Multiple Variables

Though I am partial to the Convert and Concatenate technique, there are at least three other ways in which functions can change the value of more than one variable used elsewhere in the program. Among the other ways are these:

  1. Designating variables as "GLOBAL". (Online explanation here.) The GLOBAL statement is used to identify variables that are global in scope. If a variable is global in scope, it can be seen inside of a function, and its value can be changed from within the function, even if it is not part of the result that is returned from the function. The Liberty BASIC help file encourages the programmer to exercise caution when using global variables.

  2. Passing variables to functions BYREF. (Online explanation here.) According to LB's help file, when a variable is passed "byref" to the function, a change to the value of the variable in the function will result in a change to value of the variable elsewhere in the program.

  3. Using array values in functions. By default, array values are global in scope, which means that array values are not only visible from within functions, but array values can also be changed within functions. Here's an example where this might be useful. Say that a programmer wants to use the value of pi (3.14159) within six different functions. Rather than define pi within each function, the programmer can store the value of pi in an array variable at the top of her program:

    dim pi(1)
    pi(1) = 3.14159
    

    Thereafter, whenever a function needs the value of pi, all it has to do is reference the array value, pi(1).

I have no doubts that there are additional clever ways in which programmers have been using their creativity to get functions to peform feats of skill not originally imagined. If so, speak up in the Liberty BASIC Forum!


--- Tom Nally


NL133 Home

::::::::::::::::::::::::::::::::::::::::::::::::::::

Chat Challenge

Eddie Version 3

Stylebits Corner

Progress Bars

Velleman Interface

Sprite Byte

Simulating BMP Buttons

Program Security

LB Functions

::::::::::::::::::::::::::::::::::::::::::::::::::::

Submission Guildlines

Newsletter Help

Index